﻿#pragma once
#include <coroutine>
#include <exception>
#include <iostream>
#include <iterator>
#include <utility>

#include "cstr_util.hxx"
#include "acis/boolapi.hxx"
#include "acis/intrapi.hxx"

// FORCE_RUN_ALL 忽略所有测试跳过
//#define FORCE_RUN_ALL

// 测试的布尔操作
constexpr std::initializer_list<BOOL_TYPE> bool_types = { UNION, INTERSECTION, SUBTRACTION };
// 用于控制是否运行全量测试
#ifdef FULL_BATCH_TEST
const int BATCH_TEST_NUM = 100;
const double TEST_PROBABLITY = 1.1;
#else
const int BATCH_TEST_NUM = 100;
const double TEST_PROBABLITY = 0.01;
#endif
/**
 * @brief 用于记录批量测试中跳过的步骤的原因
 */
enum batch_test_skip_reason {
	acis_pass_atom_pass,  // ACIS通过，ATOM通过
	acis_pass_atom_fail,  // ACIS通过，ATOM失败
	acis_fail_atom_pass,  // ACIS失败，ATOM通过
	acis_fail_atom_fail,  // ACIS失败，ATOM失败
};
/**
 * @brief 用于记录ATOM基本体批量测试中跳过的步骤的信息
 * 通配符使用方式，如跳过e1为E_WIGGLE的所有情况，可以使用
 * BatchTestSkipInfo(E_WIGGLE, E_ALL, acis_pass_atom_fail, 100);
 */
struct BatchTestSkipInfo {
	BASIC_ENTITIES e1;                   // 第一个实体
	BASIC_ENTITIES e2;                   // 第二个实体
	batch_test_skip_reason skip_reason;  // 跳过的原因
	int batch_test_num;                  // 批测试数
	std::vector<int> failed_index_list;  // 失败的索引列表
	BOOL_TYPE bool_type;                 // 布尔类型

	BatchTestSkipInfo(BASIC_ENTITIES e1, BASIC_ENTITIES e2, batch_test_skip_reason skip_reason, int test_num) : e1(e1), e2(e2), skip_reason(skip_reason), batch_test_num(test_num), bool_type(UNION) {}
	BatchTestSkipInfo(BASIC_ENTITIES e1, BASIC_ENTITIES e2, batch_test_skip_reason skip_reason, int test_num, std::vector<int> failed_index_list)
		: e1(e1), e2(e2), skip_reason(skip_reason), batch_test_num(test_num), failed_index_list(failed_index_list), bool_type(UNION) {}
	BatchTestSkipInfo(BASIC_ENTITIES e1, BASIC_ENTITIES e2, batch_test_skip_reason skip_reason, int test_num, std::vector<int> failed_index_list, BOOL_TYPE bool_type)
		: e1(e1), e2(e2), skip_reason(skip_reason), batch_test_num(test_num), failed_index_list(failed_index_list), bool_type(bool_type) {}
};
// 以下接口用于创建需要跳过的元素集合
logical is_skipped(BASIC_ENTITIES e1, BASIC_ENTITIES e2, int cur_index, BOOL_TYPE bool_type, std::vector<BatchTestSkipInfo>& skip_list);

// ATOM全量测试元素
constexpr std::initializer_list<BASIC_ENTITIES> bool_elements = { E_CUBE,    E_SPHERE,   E_CYLINDER,   E_TORUS,         E_CONE,   E_PRISM,  E_PYRAMID,         E_WIGGLE,           E_PLANE,         E_PPLANE,         E_CPLANE,
																 E_SPHERES, E_PSPHERES, E_CONIC_SIDE, E_CYLINDER_SIDE, E_PCONIC, E_TORUSS, E_PTORUSS,         E_BSPLINES_CTRLPTS, E_BSPLINES_FIT,  E_BSPLINES_INTRP, E_LINE,
																 E_ARC,     E_ELLIPSE,  E_CONIC,      E_HELIX,         E_SPIRAL, E_SPRING, E_BSPLINE_CTRLPTS, E_BSPLINE_FIT,      E_BSPLINE_INTRP, E_BEZIER };

// Boolean_BatchApiBooleanTests.Test_*_0
// Boolean_BatchApiBooleanTests.Test_*_1
// Boolean_BatchApiBooleanTests.Test_*_2
#ifdef FORCE_RUN_ALL
static std::vector<BatchTestSkipInfo> total_boolean_basic_skipped_cases = {};
#else
static std::vector<BatchTestSkipInfo> total_boolean_basic_skipped_cases = {
	// BatchTestSkipInfo(E_*, E_*, acis_pass_atom_fail, 100),
};
#endif

// Boolean_BatchApiBooleanTests.Test_*_Translate
#ifdef FORCE_RUN_ALL
static std::vector<BatchTestSkipInfo> total_boolean_move_skipped_cases = {};
#else
static std::vector<BatchTestSkipInfo> total_boolean_move_skipped_cases = {};
#endif

// Boolean_BatchApiBooleanTests.Test_*_Scale

#ifdef FORCE_RUN_ALL
static std::vector<BatchTestSkipInfo> total_boolean_scale_skipped_cases = {};
#else
static std::vector<BatchTestSkipInfo> total_boolean_scale_skipped_cases = {};
#endif

// Boolean_BatchApiBooleanTests.Test_*_Rotate
#ifdef FORCE_RUN_ALL
static std::vector<BatchTestSkipInfo> total_boolean_rotate_skipped_cases = {};
#else
static std::vector<BatchTestSkipInfo> total_boolean_rotate_skipped_cases = {
	// BatchTestSkipInfo(E_*, E_*, acis_fail_atom_pass, 100, std::vector<int>() = {2}, SUBTRACTION),
};
#endif

/**
 * @brief check same body with (api_clean_entity/atom_api_clean_entity).
 * @param blank_ACIS
 * @param blank_ATOM
 * @param check_entity
 */
bool check_same_body_with_clean(BODY* blank_ACIS, BODY* blank_ATOM, bool check_entity);

template <typename Ty> struct Generator {
	struct promise_type {
		Generator get_return_object() { return Generator{ *this }; }
		auto initial_suspend() noexcept { return std::suspend_always{}; }
		auto final_suspend() noexcept { return std::suspend_always{}; }

		void return_void() noexcept {}

		std::suspend_always yield_value(const Ty& value) noexcept {
			current_value_ = std::addressof(value);
			return {};
		}

		void unhandled_exception() noexcept { exception_ = std::current_exception(); }

		const Ty* current_value_;
		std::exception_ptr exception_;
	};

	struct iterator {
		using iterator_category = std::input_iterator_tag;
		using difference_type = std::ptrdiff_t;
		using value_type = Ty;
		using reference = const Ty&;
		using pointer = const Ty*;

		std::coroutine_handle<promise_type> coro_ = nullptr;

		iterator() = default;
		explicit iterator(std::coroutine_handle<promise_type> coro) noexcept : coro_(coro) {}

		iterator& operator++() {
			coro_.resume();
			if (coro_.done()) {
				coro_ = nullptr;
			}
			return *this;
		}

		void operator++(int) { (*this)++; }

		bool operator==(const iterator& right) const noexcept { return coro_ == right.coro_; }

		bool operator!=(const iterator& right) const noexcept { return !(*this == right); }

		reference operator*() const noexcept { return *coro_.promise().current_value_; }

		pointer operator->() const noexcept { return coro_.promise().current_value_; }
	};

	iterator begin() {
		if (coro_handle_) {
			coro_handle_.resume();
			if (coro_handle_.done()) {
				return {};
			}
		}

		return iterator{ coro_handle_ };
	}

	iterator end() noexcept { return {}; }

	using handle = std::coroutine_handle<promise_type>;

	explicit Generator(promise_type& prom) noexcept : coro_handle_(std::coroutine_handle<promise_type>::from_promise(prom)) {}

	Generator(Generator&& rhs) noexcept : coro_handle_(std::exchange(rhs.coro_handle_, nullptr)) {}

	Generator& operator=(Generator&& right) noexcept {
		coro_handle_ = std::exchange(right.coro_handle_, nullptr);
		return *this;
	}

	~Generator() {
		if (coro_handle_) coro_handle_.destroy();
	}

private:
	explicit Generator(handle h) : coro_handle_(h) {}
	handle coro_handle_ = nullptr;
};

struct IdentityTransfGenerator {
	static SPAtransf alignmentTransformation(BODY* tool, BODY* blank) { return identity_transform; }

	static Generator<std::pair<SPAtransf, int>> transfGenerator(BODY* tool, BODY* blank) { co_yield{ identity_transform, 0 }; }
};

struct TranslationGenerator {
	static SPAtransf alignmentTransformation(BODY* tool, BODY* blank) {
		SPAposition min_pt1(0, 0, 0), max_pt1(0, 0, 0), min_pt2(0, 0, 0), max_pt2(0, 0, 0);
		SPAboxing_options* box_opts = ACIS_NEW SPAboxing_options(nullptr, SPAboxing_tight);
		api_get_entity_box((ENTITY*)tool, min_pt1, max_pt1, box_opts);
		api_get_entity_box((ENTITY*)blank, min_pt2, max_pt2, box_opts);

		ACIS_DELETE box_opts;
		SPAvector v0 = min_pt1 - max_pt2;

		return translate_transf(v0);
	}

	static Generator<std::pair<SPAtransf, int>> transfGenerator(BODY* tool, BODY* blank) {
		SPAposition min_pt1(0, 0, 0), max_pt1(0, 0, 0), min_pt2(0, 0, 0), max_pt2(0, 0, 0);
		SPAboxing_options* box_opts = ACIS_NEW SPAboxing_options(nullptr, SPAboxing_tight);
		api_get_entity_box((ENTITY*)tool, min_pt1, max_pt1, box_opts);
		api_get_entity_box((ENTITY*)blank, min_pt2, max_pt2, box_opts);

		ACIS_DELETE box_opts;
		SPAvector v0 = min_pt1 - max_pt2;
		SPAvector v1 = max_pt1 - min_pt1 + max_pt2 - min_pt2;

		int trans_num_threshold = BATCH_TEST_NUM;  // 平移数目上限
		float step_ratio_threshold = 0.1;          // 最大平移率
		bool uniform_sampling = false;             // 平移策略：是否匀步平移

		// 如果实体包围盒为0，则不再平移
		if (v1.len() == 0) trans_num_threshold = 1;
		SPAvector trans = SPAvector(0.0, 0.0, 0.0);
		SPAtransf t = identity_transform;

		for (int i = 0; i < trans_num_threshold;) {
			co_yield{ t, i };

			if (uniform_sampling)
				i += 1;
			else {
				if (i > trans_num_threshold * step_ratio_threshold && i < (1 - step_ratio_threshold) * trans_num_threshold) {
					i += trans_num_threshold * step_ratio_threshold;
					trans += step_ratio_threshold * v1;
				}
				else {
					i += 1;
					trans += v1 / trans_num_threshold;
				}
			}
			t = translate_transf(v0 + trans);
		}
	}
};

struct ScaleGenerator {
	static SPAtransf alignmentTransformation(BODY* tool, BODY* blank) {
		WCS* wcs1 = nullptr;
		SPAposition min_pt1(0, 0, 0), max_pt1(0, 0, 0), min_pt2(0, 0, 0), max_pt2(0, 0, 0);
		SPAboxing_options* box_opts = ACIS_NEW SPAboxing_options(wcs1, SPAboxing_tight);
		api_get_entity_box((ENTITY*)tool, min_pt1, max_pt1, box_opts);
		api_get_entity_box((ENTITY*)blank, min_pt2, max_pt2, box_opts);
		ACIS_DELETE box_opts;
		SPAvector v0 = min_pt1 - min_pt2;
		SPAvector v1 = max_pt1 - min_pt1;
		SPAvector v2 = max_pt2 - min_pt2;

		// 对齐包围盒
		SPAtransf t = translate_transf(v0);

		double cur_scale = 0.0;
		if (v2.len()) {
			cur_scale = v1.len() / v2.len();
			t *= scale_transf(cur_scale);
		}

		return t;
	}

	static Generator<std::pair<SPAtransf, int>> transfGenerator(BODY* tool, BODY* blank) {
		WCS* wcs1 = nullptr;
		SPAposition min_pt1(0, 0, 0), max_pt1(0, 0, 0), min_pt2(0, 0, 0), max_pt2(0, 0, 0);
		SPAboxing_options* box_opts = ACIS_NEW SPAboxing_options(wcs1, SPAboxing_tight);
		api_get_entity_box((ENTITY*)tool, min_pt1, max_pt1, box_opts);
		api_get_entity_box((ENTITY*)blank, min_pt2, max_pt2, box_opts);
		ACIS_DELETE box_opts;
		SPAvector v0 = min_pt1 - min_pt2;
		SPAvector v1 = max_pt1 - min_pt1;
		SPAvector v2 = max_pt2 - min_pt2;
		int scale_num_threshold = BATCH_TEST_NUM;  // 缩放数目上限
		float step_ratio_threshold = 0.1;          // 最大缩放率
		bool uniform_sampling = false;             // 缩放策略：是否匀步缩放

		double cur_scale = 1.0;
		double accumulated_scale = 1.0;
		if (v2.len()) {
			cur_scale = v1.len() / v2.len();
		}
		else
			scale_num_threshold = 1;

		SPAtransf t = identity_transform;

		for (int i = 1; i < scale_num_threshold;) {
			co_yield{ t, i };

			accumulated_scale = double(i) / 10;

			if (uniform_sampling)
				i += 1;
			else {
				if (i > scale_num_threshold * step_ratio_threshold && i < (1 - step_ratio_threshold) * scale_num_threshold) {
					i += scale_num_threshold * step_ratio_threshold;
				}
				else {
					i += 1;
				}
			}
			t = scale_transf(accumulated_scale);
		}
	}
};

struct ScaleGeneratorSliceVersion {
	static SPAtransf alignmentTransformation(BODY* tool, BODY* blank) {
		WCS* wcs1 = nullptr;
		SPAposition min_pt1(0, 0, 0), max_pt1(0, 0, 0), min_pt2(0, 0, 0), max_pt2(0, 0, 0);
		SPAboxing_options* box_opts = ACIS_NEW SPAboxing_options(wcs1, SPAboxing_tight);
		api_get_entity_box((ENTITY*)tool, min_pt1, max_pt1, box_opts);
		api_get_entity_box((ENTITY*)blank, min_pt2, max_pt2, box_opts);
		ACIS_DELETE box_opts;
		SPAvector v0 = min_pt1 - min_pt2;
		SPAvector v1 = max_pt1 - min_pt1;
		SPAvector v2 = max_pt2 - min_pt2;

		// 对齐包围盒
		SPAtransf t = identity_transform;

		double cur_scale = 0.0;
		double accumulated_scale = 1.0;
		if (v2.len()) {
			cur_scale = v1.len() / v2.len();
			t *= scale_transf(cur_scale);
		}

		return t;
	}

	static Generator<std::pair<SPAtransf, int>> transfGenerator(BODY* tool, BODY* blank) {
		WCS* wcs1 = nullptr;
		SPAposition min_pt1(0, 0, 0), max_pt1(0, 0, 0), min_pt2(0, 0, 0), max_pt2(0, 0, 0);
		SPAboxing_options* box_opts = ACIS_NEW SPAboxing_options(wcs1, SPAboxing_tight);
		api_get_entity_box((ENTITY*)tool, min_pt1, max_pt1, box_opts);
		api_get_entity_box((ENTITY*)blank, min_pt2, max_pt2, box_opts);
		ACIS_DELETE box_opts;
		SPAvector v0 = min_pt1 - min_pt2;
		SPAvector v1 = max_pt1 - min_pt1;
		SPAvector v2 = max_pt2 - min_pt2;
		int scale_num_threshold = BATCH_TEST_NUM;  // 缩放数目上限
		float step_ratio_threshold = 0.1;          // 最大缩放率
		bool uniform_sampling = false;             // 缩放策略：是否匀步缩放

		double cur_scale = 1.0;
		double accumulated_scale = 1.0;
		if (v2.len()) {
			cur_scale = v1.len() / v2.len();
		}
		else
			scale_num_threshold = 1;

		SPAtransf t = identity_transform;

		for (int i = 1; i < scale_num_threshold;) {
			co_yield{ t, i };

			accumulated_scale = double(i) / 10;

			if (uniform_sampling)
				i += 1;
			else {
				if (i > scale_num_threshold * step_ratio_threshold && i < (1 - step_ratio_threshold) * scale_num_threshold) {
					i += scale_num_threshold * step_ratio_threshold;
				}
				else {
					i += 1;
				}
			}
			double scale = accumulated_scale / cur_scale;
			t = scale_transf(scale);
			cur_scale = accumulated_scale;
		}
	}
};

struct RotateGenerator {
	static SPAtransf alignmentTransformation(BODY* tool, BODY* blank) {
		SPAposition min_pt1(0, 0, 0), max_pt1(0, 0, 0), min_pt2(0, 0, 0), max_pt2(0, 0, 0);
		SPAboxing_options* box_opts = ACIS_NEW SPAboxing_options(nullptr, SPAboxing_tight);
		api_get_entity_box((ENTITY*)tool, min_pt1, max_pt1, box_opts);
		api_get_entity_box((ENTITY*)blank, min_pt2, max_pt2, box_opts);

		ACIS_DELETE box_opts;
		SPAvector v0 = min_pt1 - max_pt2;

		return translate_transf(v0);
	}

	static Generator<std::pair<SPAtransf, int>> transfGenerator(BODY* tool, BODY* blank) {
		WCS* wcs1 = nullptr;
		SPAposition min_pt1(0, 0, 0), max_pt1(0, 0, 0), min_pt2(0, 0, 0), max_pt2(0, 0, 0);
		SPAboxing_options* box_opts = ACIS_NEW SPAboxing_options(wcs1, SPAboxing_tight);
		api_get_entity_box((ENTITY*)tool, min_pt1, max_pt1, box_opts);
		api_get_entity_box((ENTITY*)blank, min_pt2, max_pt2, box_opts);
		ACIS_DELETE box_opts;
		SPAvector v0 = min_pt1 - max_pt2;
		SPAvector v2 = max_pt2 - min_pt2;
		int rotate_num_threshold = BATCH_TEST_NUM;  // 旋转数目上限
		float step_ratio_threshold = 0.1;           // 最大偏移率
		bool uniform_sampling = false;              // 旋转策略：是否匀步旋转
		SPAtransf t = identity_transform;

		double angle = 2.0 * M_PI / rotate_num_threshold;

		for (int i = 0; i < rotate_num_threshold;) {
			co_yield{ t, i };

			if (uniform_sampling)
				i += 1;
			else {
				// 根据需要定制旋转策略，此处采用简单的两端临界详测，中间简测方式。
				if (i > rotate_num_threshold * step_ratio_threshold && i < (1 - step_ratio_threshold) * rotate_num_threshold) {
					i += rotate_num_threshold * step_ratio_threshold;
					angle += rotate_num_threshold * step_ratio_threshold * 2.0 * M_PI / rotate_num_threshold;
				}
				else {
					i += 1;
					angle += 2.0 * M_PI / rotate_num_threshold;
				}
			}
			t = rotate_transf(angle, v2);
		}
	}
};

template <typename T>
concept IsTransfGenerator = requires {
	{ T::alignmentTransformation((BODY*)nullptr, (BODY*)nullptr) } -> std::same_as<SPAtransf>;
	{ T::transfGenerator((BODY*)nullptr, (BODY*)nullptr) } -> std::same_as<Generator<std::pair<SPAtransf, int>>>;
};
